/******************************************************************************
秋夜静，独坐对残灯。
啼笑非非谁识我，坐行梦梦尽缘君，何所慰消沉。
风卷雨，雨复卷侬心。
心似欲随风雨去，茫茫大海任浮沉。
无爱亦无恨。
******************************************************************************/
#include "kbRGB_Int.h"
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "kbRGB_Int_Effect.h"

static hsv_t RandomHSVColor()
{
    hsv_t hsv;

    hsv.hue = kb_rand() % 360;
    hsv.saturation = 255;
    hsv.value = 255;

    return hsv;
}
static uint32_t RandomRGBColor()
{
    hsv_t hsv = RandomHSVColor();
    return (hsv2rgb(&hsv));
}
static uint32_t Enlight(uint32_t color, float value)
{
    hsv_t hsv;
    rgb2hsv(color, &hsv);

    hsv.value *= value;

    return (hsv2rgb(&hsv));
}
static unsigned char InterpolateChanel(unsigned char a, unsigned char b, float x)
{
    return (int) ((b - a) * x + a);
}
#define RGBGetRValue(rgb)   (rgb & 0x000000FF)
#define RGBGetGValue(rgb)   ((rgb >> 8) & 0x000000FF)
#define RGBGetBValue(rgb)   ((rgb >> 16) & 0x000000FF)
#define ToRGBColor(r, g, b) ((uint32_t)((b << 16) | (g << 8) | (r)))
static uint32_t Interpolate(uint32_t color1, uint32_t color2, float fraction)
{
    return ToRGBColor(
                InterpolateChanel(RGBGetRValue(color1), RGBGetRValue(color2), fraction),
                InterpolateChanel(RGBGetGValue(color1), RGBGetGValue(color2), fraction),
                InterpolateChanel(RGBGetBValue(color1), RGBGetBValue(color2), fraction)
                );
}
/*************************************************************************************************\
|        Breathing [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Int_Breathing_Init(struct kbRGB_Int_Params *const params)    // 呼吸
{
    params->Speed = 5;
    params->Slider2Val = 30;
}

void kbRGB_Int_Breathing(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 呼吸
{
    uint32_t color;
    params->Progress += (params->Speed + speed * 5 )*180/(double)(10*KBRGB_PI*params->FPS);
     if(params->Progress >= 360) // PI
     {
         params->Progress -= 360;
         params->hsv = RandomHSVColor();
     }

    //CurrentColor.value = pow(sin(kbRGB_progress),3) * 255;
    double _sin = kbRGB_Sin_Int(params->Progress);
    _sin = _sin/(float)kbRGB_SinMax; params->hsv.value = pow(_sin, 3) * 255;
    color = hsv2rgb(&params->hsv);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
           kbRGB_pset(col_id, row_id, color);
        }
    }
}

/*************************************************************************************************\
|        BreathingCircle [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Int_BreathingCircle_Init(struct kbRGB_Int_Params *const params)    // 呼吸循环
{
    params->Speed = 5;
    params->Slider2Val = 3;
}
static uint32_t BreathingCircle_GetColor(const uint16_t x, const uint16_t y, const int16_t w, const int16_t h, const int32_t _progress)
{
    //float distance = std::min<float>(1.0, sqrt(pow(0.5 - x/w, 2) + pow(0.5 - y/h, 2)));
    //float distance = sqrt(pow(0.5 - x/(float)w, 2) + pow(0.5 - y/(float)h, 2));
    //float distance = sqrt(pow(((w>>1) - x)/(float)w, 2) + pow(((h>>1) - y)/(float)h, 2));
//    int16_t dx = (w>>1) - x;
//    int16_t dy = (h>>1) - y;
	int16_t dx = w - x*2;
    int16_t dy = h - y*2;
    float distance = kb_sqrt((dx*dx*(0x1<<16))/(float)(w*w) + (dy*dy)*(0x1<<16)/(float)(h*h)) / (float)(0x1<<8);
	distance = distance/2;
    if(distance>1.0f) distance=1.0f;
    distance = distance/1.5;
    hsv_t tmp;
    //tmp.hue = pow(sin(progress),3) * 255;
    //tmp.hue = (int) (360 * (_progress/(float)kbRGB_SinMax+distance) + 0) % 360;
    //tmp.hue = (int) (360 * ((_progress+distance*kbRGB_SinMax)/(float)kbRGB_SinMax) + 0) % 360;
    tmp.hue = (((int)(360 * (_progress+distance*kbRGB_SinMax))>>8) + 360) % 360;
    tmp.saturation =  255;
    tmp.value =  255;
//    if(distance > progress || distance < progress - kbRGB_Slider2Val/(0.5*(w+h)))
//        return 0x00;//ColorUtils::OFF();
    //tmp.value =  255*progress;

    //return UserColors[0];
    //return /*ColorUtils::*/RandomRGBColor();
    return hsv2rgb(&tmp);
}
void kbRGB_Int_BreathingCircle(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 呼吸循环
{
    int16_t progress = (kbRGB_SinMax + kbRGB_Sin_Int( (params->Progress)*2/10 ))>>1;
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = BreathingCircle_GetColor(col_id, row_id, cols-1, rows-1, reverse?progress:-progress);
            kbRGB_pset(col_id, row_id, color);
        }
    }
    params->Progress += (params->Speed + speed*3 )*180 / (double) (params->FPS*KBRGB_PI);
}

/*************************************************************************************************\
|        Comet [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Int_Comet_Init(struct kbRGB_Int_Params *const params)    // 彗星
{
    params->Speed = 15;
    params->Slider2Val = 50;
}
static uint32_t Comet_GetColor(const uint32_t i, const uint16_t width, const uint32_t position, const uint32_t progress, const unsigned int Slider2Val)
{
    //float comet_size = 0.01 * Slider2Val * width;
    const uint32_t comet_size = Slider2Val * width;

    //uint32_t position = _progress * 2 * width;
    hsv_t tmp;

    if(i > position) //if(i > position)
    {
        return 0x00;//ColorUtils::OFF();
    }

    float distance = (position - i)*100;

    uint32_t value;

    if(distance > comet_size)
    {
        value = 0;
    }
    else if(distance == 0)
    {
        value = 1*255;
    }
    else
    {
        value = 255 - (distance / comet_size)*255;
    }
    tmp.hue = (int) (progress + (distance * 360 / comet_size)) % 360;
    tmp.saturation = (value*value) >> 8;
    tmp.value = (value);

    return (hsv2rgb(&tmp));
}
void kbRGB_Int_Comet(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 彗星
{
    //int reverse = 0;
    int32_t position = (int32_t)params->Progress;
    position = (float)(params->Progress-(float)position) * 2 * cols;
    //printf("[%s--%d] position:%5d Progress:%8f \r\n", __func__, __LINE__, position, params->Progress); fflush(stdout);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Comet_GetColor(reverse ? cols - col_id - 1: col_id, cols, position, 5*params->Progress, params->Slider2Val);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    params->Progress += (params->Speed+speed*5) / (double)(params->FPS*100);
}

/*************************************************************************************************\
|        Fill [9]                                                                              |
\*************************************************************************************************/
//static double old_progress = 0;
//static uint32_t random;
//#define old_progress kbrgb_Int_Params.Simple_Fill_old_progress
void kbRGB_Int_Fill_Init(struct kbRGB_Int_Params *const params)    // 充满
{
    params->Speed = 50;
    params->Slider2Val = 30;
    params->Simple_Fill_old_progress = 0;
    params->UserColors[0] = RandomRGBColor();
}
static uint32_t Fill_GetColor(const uint16_t idx, const uint16_t width, const uint32_t progress, const uint32_t random)
{
//    double position = _progress/(float)kbRGB_PRE_MAX * (width+3);
//    double distance = position - idx;
    uint32_t position = progress * (width+3);
    //double distance = position/(float)kbRGB_PRE_MAX - idx;
    double distance = position/(float)kbRGB_PRE_MAX - idx;
    distance = distance/3;

    if(distance > 1)
    {
        return random;//color;
    }
    if(distance > 0) return Enlight(random, distance);
    return 0x00;
}
void kbRGB_Int_Fill(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 充满
{
    //time += 0.01 * Speed / (float) FPS;
    params->Progress += (params->Speed+speed*10) / (double)(params->FPS*100);
    //double whole;
    //progress = modf(kbRGB_time, &whole);
    int32_t progress = params->Progress;
    progress = (params->Progress-progress)*(float)kbRGB_PRE_MAX;

    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Fill_GetColor(reverse ? cols - col_id - 1: col_id, cols, progress, params->UserColors[0]);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }

    if(params->Simple_Fill_old_progress > progress/* && RandomColorsEnabled*/)
    {
        params->UserColors[0] = /*ColorUtils::*/RandomRGBColor();
    }
    params->Simple_Fill_old_progress = progress;
}

/*************************************************************************************************\
|        Marquee [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Int_Marquee_Init(struct kbRGB_Int_Params *const params)    // 大天幕
{
    params->Speed = 30;
    params->Slider2Val = 2;
    params->hsv.saturation = 255;
    params->hsv.value = 255;
    params->hsv.hue = 0;
}
static uint32_t Marquee_GetColor(const unsigned int i, const double progress, const unsigned int Slider2Val, const uint32_t color)
{
    unsigned int spacing = Slider2Val;
    //double kk = (int)progress;
    //kk = progress-kk;
    return ((i +  ((int)progress)) % spacing == 0) ? (color) : 0x00;
    //return ((i +  ((int)progress)) % spacing == 0) ? Enlight(color, 1.0-kk) : Enlight(color, kk);
}
void kbRGB_Int_Marquee(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 大天幕
{
    const uint32_t color1 = hsv2rgb(&params->hsv);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Marquee_GetColor(reverse ? cols - col_id - 1: col_id, params->Progress, params->Slider2Val, color1);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    //progress += 0.1 * (Speed<<kbRGB_PRE_BIT) / (float) FPS;
    params->Progress += (params->Speed + speed*10) / (double)(params->FPS*10);
    params->hsv.hue++;
    params->hsv.hue%=360;
}

/*************************************************************************************************\
|        Mosaic [9]                                                                               |
\*************************************************************************************************/
void kbRGB_Int_Mosaic_Init(struct kbRGB_Int_Params *const params)    // 马赛克
{
    params->Speed = 10;
    params->Slider2Val = 30;
    memset(params->Simple_Mosaic_tiles, 0x00, sizeof(params->Simple_Mosaic_tiles));
}
// 为了节约内存选择牺牲算力
static uint32_t Mosaic_GetColor(const uint32_t old_rgb, const unsigned int rarity, const uint16_t speed, struct kbRGB_Mosaic_t* const tile)
{
    //unsigned int rarity = Slider2Val;
    hsv_t hsv;
    rgb2hsv(old_rgb, &hsv);
    if(tile->brightness <= 255)//if(tile->brightness <= 0.f)
    {
        if(kb_rand() % rarity == 0)
        {
            //tile->brightness = 1.f;
            tile->brightness = 0xFFFF;//255.f;
            //tile->decrease_speed_mult = ((double) kb_rand() / (RAND_MAX)) + 1;
            tile->decrease_speed_mult = (kb_rand()&0xFF) + 255;
            hsv = RandomHSVColor();
        }
    }
//    tile->brightness -= 0.0005*255 * kbRGB_Speed * tile->decrease_speed_mult;
//    hsv.value = tile->brightness > 0 ?tile->brightness : 0;
    //if(tile->brightness>=255) tile->brightness -= 0.0005*255 * speed * tile->decrease_speed_mult;
    if(tile->brightness>=255) tile->brightness -= 5*255 * speed * tile->decrease_speed_mult/10000;
    hsv.value = tile->brightness > 255 ?(tile->brightness>>8) : 0;
    return hsv2rgb(&hsv);
}
void kbRGB_Int_Mosaic(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 马赛克
{
    //Mosaic_UpdateTiles();
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
           uint16_t idx = (row_id * cols) + col_id;
           uint32_t color = Mosaic_GetColor(kbRGB_get_point(col_id, row_id), params->Slider2Val, params->Speed+speed*3, &params->Simple_Mosaic_tiles[idx]);
           kbRGB_pset(col_id, row_id, color);
        }
    }
}

/*************************************************************************************************\
|        MotionPoint [9]                                                                              |
\*************************************************************************************************/
//static uint32_t current=0x00;
//#define current kbrgb_Int_Params.UserColors[0]
void kbRGB_Int_MotionPoint_Init(struct kbRGB_Int_Params *const params)    // 运动点
{
    params->Speed = 25-5;
    params->Slider2Val = 30;
    uint32_t current  = RandomRGBColor();
    rgb2hsv(current, &params->hsv);
    params->progress = -(90<<kbRGB_PRE_BIT);
}
static uint32_t MotionPoint_GetColor(unsigned int w, const unsigned int x, const double t, const hsv_t * const _hsv)
{
    hsv_t hsv;
    if(w == 0)
    {
        w = 1;
    }

    float distance = fabs(x - t * (w-1));

//    if(distance > 2)
//    {
//        return 0x00;//background;
//    }
    distance = (w-distance)/(float)w;

    hsv.hue = _hsv->hue;
    hsv.saturation = _hsv->saturation;
    hsv.value = _hsv->value*distance*distance*distance;
    return hsv2rgb(&hsv);
}
void kbRGB_Int_MotionPoint(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 运动点
{
    //double t = (1 + sin(kbRGB_progress)) / 2.f;
    double t = (kbRGB_SinMax + kbRGB_Sin_Int(params->progress>>(kbRGB_PRE_BIT-KBRGB_COS_PRE))) / (2.f*kbRGB_SinMax);
    //uint32_t current  = hsv2rgb(&params->hsv);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = MotionPoint_GetColor(cols, col_id, t, &params->hsv);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    //kbRGB_progress += 0.1 * (float) kbRGB_Speed / (float) kbRGB_FPS;
    //progress += (0.1*180/KBRGB_PI) * (float) (Speed<<kbRGB_PRE_BIT) / (float) FPS;
    params->progress += (180) * ((params->Speed+speed*5)<<kbRGB_PRE_BIT) / (int) (params->FPS*KBRGB_PI*10);
    if(params->progress>=(360<<kbRGB_PRE_BIT))//if(progress>=180)
    {
        params->progress -= (360<<kbRGB_PRE_BIT);
    }
    params->hsv.hue++;
    params->hsv.hue%=360;
}

/*************************************************************************************************\
|        MotionPoints [9]                                                                              |
\*************************************************************************************************/
static void PointMove(struct kbRGB_Int_MovingPoint* const point, const uint16_t delta, const uint16_t cols, const uint16_t rows)
{
    switch(point->dir)
    {
        case LEFT:
        {
            if(point->x>=0) point->x -= delta;
            break;
        }

        case RIGHT:
        {
            if(point->x<=(cols<<kbRGB_PRE_BIT)) point->x += delta;
            break;
        }

        case UP:
        {
            if(point->y>=0) point->y -= delta;
            break;
        }

        case DOWN:
        {
            if(point->y<=(rows<<kbRGB_PRE_BIT)) point->y += delta;
            break;
        }
    }
}

static int PointIsOut(struct kbRGB_Int_MovingPoint* const point, const uint16_t cols, const uint16_t rows)
{
    return (point->x < 0) || (point->y < 0) || (point->x > (cols<<kbRGB_PRE_BIT)) || (point->y > (rows<<kbRGB_PRE_BIT));
}

static void PointInit(struct kbRGB_Int_MovingPoint* const point, const uint32_t c, const uint16_t cols, const uint16_t rows)
{
    point->dir = kb_rand() % 4;//static_cast<Dir>(kb_rand() % 4);
    point->speed_mult = ( kb_rand() & (kbRGB_PRE_MAX>>3)) + 16;
    point->color = c;

    switch(point->dir)
    {
        case LEFT:
        {
            point->x = cols<<kbRGB_PRE_BIT;
            point->y = ( kb_rand() % rows)<<kbRGB_PRE_BIT;
            break;
        }

        case RIGHT:
        {
            point->x = 0;
            point->y = ( kb_rand() % rows)<<kbRGB_PRE_BIT;
            break;
        }

        case UP:
        {
            point->x = ( kb_rand() % cols)<<kbRGB_PRE_BIT;
            point->y = rows<<kbRGB_PRE_BIT;
            break;
        }

        case DOWN:
        {
            point->x = ( kb_rand() % cols)<<kbRGB_PRE_BIT;
            point->y = 0;
            break;
        }
    }
}

static void MotionPoints_SetSlider2Val(struct kbRGB_Int_MovingPoint points[], const uint16_t pointsSize, const uint16_t cols, const uint16_t rows)
{
    //Slider2Val = pointsSize;
    for(unsigned int i = 0; i < pointsSize; i++)
    {
        PointInit(&points[i], RandomRGBColor(), cols, rows);
    }
}
void kbRGB_Int_MotionPoints_Init(struct kbRGB_Int_Params *const params)    // 运动点
{
    params->Speed = 50;
    params->Slider2Val = kbRGB_pointsSize;//4;
    MotionPoints_SetSlider2Val(params->Simple_points, kbRGB_pointsSize, 41, 41);
}
static void MotionPoints_UpdatePoints(struct kbRGB_Int_MovingPoint points[], const uint16_t pointsSize, const uint16_t cols, const uint16_t rows, const unsigned int Speed)
{
    for(uint16_t i=0; i<pointsSize; i++)//for(MovingPoint& point: points)
    {
        //PointMove(&points[i], 0.0001 * (Speed<<kbRGB_PRE_BIT) * points[i].speed_mult, cols, rows);
        PointMove(&points[i], (Speed<<kbRGB_PRE_BIT) * points[i].speed_mult/10000, cols, rows);

        if(PointIsOut(&points[i], cols, rows))
        {
            //point.Init(RandomColorsEnabled ? ColorUtils::RandomRGBColor() : UserColors[kb_rand() % UserColors.size()]);
            PointInit(&points[i], RandomRGBColor(), cols, rows);
        }
    }
}

static uint32_t MotionPoints_GetColor(const float x, const float y, const float w, const float h, const struct kbRGB_Int_MovingPoint points[], const uint16_t pointsSize)
{
    //struct kbRGB_Pixel pix;
    for(uint16_t i=0; i<pointsSize; i++)//for(MovingPoint& point: points)
    {
       //float distance = sqrt(pow((points[i].x>>kbRGB_PRE_BIT) - x, 2) + pow((points[i].y>>kbRGB_PRE_BIT) - y, 2));
        //pix.pixel = kbRGB_Angle_IntGet((points[i].x>>kbRGB_PRE_BIT) - x, (points[i].y>>kbRGB_PRE_BIT) - y);
        //float distance = pix.radius/4.0f;
		float distance = kbRGB_XY2Radius((points[i].x>>kbRGB_PRE_BIT) - x, (points[i].y>>kbRGB_PRE_BIT) - y)/(float)(0x1<<KBRGB_XY2RADIUS_PRE);
       if(distance <= 1)
       {
           return /*ColorUtils::*/Enlight(points[i].color, 1 - distance);
       }
    }
    return 0x00;//ColorUtils::OFF();
}
void kbRGB_Int_MotionPoints(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 运动点
{
    MotionPoints_UpdatePoints(params->Simple_points, kbRGB_pointsSize, cols, rows, params->Speed);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = MotionPoints_GetColor(col_id, row_id, cols, rows, params->Simple_points, kbRGB_pointsSize);
            kbRGB_pset(col_id, row_id, color);
        }
    }
}

/*************************************************************************************************\
|        Stack [9]                                                                              |
\*************************************************************************************************/
static uint32_t Stack_ResetZone(const uint16_t cols, struct kbRGB_Int_Params *const params)
{
    params->Simple.stops = cols-1;
    uint32_t zone_colors = RandomRGBColor();
    return zone_colors;
}
void kbRGB_Int_Stack_Init(struct kbRGB_Int_Params *const params)    // 堆
{
    params->Speed = 10;
    params->Slider2Val = 30;
    params->Simple.horizontal = 0;
    params->UserColors[0] = Stack_ResetZone(KB_COLS, params);
}
static uint32_t Stack_GetColor(const unsigned int led_idx, const unsigned int stops, const float zone_progress, const uint32_t zone_colors)
{
    unsigned int stop = stops;

    if(stop < led_idx)
    {
        return zone_colors;
    }

    //float zone_progress = kbRGB_progress;

    //float distance = fabs(zone_progress/(float)kbRGB_PRE_MAX - led_idx);
    float distance = fabs(zone_progress - led_idx);

    if(distance > 1)
    {
        return 0x00;//ColorUtils::OFF();
    }

    return /*ColorUtils::*/Enlight(zone_colors, 1 - distance);
}
void kbRGB_Int_Stack(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 堆
{
    //const int reverse=params->reverse;
    // horizontal
    const int horizontal=params->Simple.horizontal;
    float zone_progress = params->progress/(float)kbRGB_PRE_MAX;
    if(0==horizontal)
    {
        for (uint16_t col_id = 0; col_id < cols; col_id++)
        {
            uint32_t color = Stack_GetColor(reverse ? cols - col_id - 1: col_id, params->Simple.stops, zone_progress, params->UserColors[0]);
            //current_colors[col_id] = color;

            for (uint16_t row_id = 0; row_id < rows; row_id++)
            {
                kbRGB_pset(col_id, row_id, color);
            }
        }
    }
    // vertical
    else
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = Stack_GetColor(reverse ? rows - row_id - 1: row_id, params->Simple.stops, zone_progress, params->UserColors[0]);
            //current_colors[row_id] = color;

            for (uint16_t col_id = 0; col_id < cols; col_id++)
            {
                kbRGB_pset(col_id, row_id, color);
            }
        }
    }
    //float delta_progress = 0.1 * (Speed<<kbRGB_PRE_BIT) / (float) FPS;
    //progress += delta_progress * cols;//(sizeof(current_colors)/sizeof(current_colors[0]));
    params->progress += cols * ((params->Speed+speed*3)<<kbRGB_PRE_BIT) / (params->FPS*10);

    if(params->progress >= (params->Simple.stops<<kbRGB_PRE_BIT))
    {
        params->Simple.stops--;
        if(params->Simple.stops == 0)
        {
            if(0==horizontal) params->UserColors[0] = Stack_ResetZone(cols, params);
            else params->UserColors[0] = Stack_ResetZone(rows, params);
        }
        params->progress = 0;
    }
}
/*************************************************************************************************\
|        Swap [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Int_Swap_Init(struct kbRGB_Int_Params *const params)    // 交换
{
    struct kbRGB_Int_Swap_t *const Swap = &params->Simple_Swap;
    params->Speed = 10;
    params->Slider2Val = 30;
    params->Simple.old_dir = 0;
    Swap->random1 = RandomRGBColor();
    Swap->random2 = RandomRGBColor();
}
static uint32_t Swap_GetColor(const unsigned int i, const unsigned int w, const int32_t _progress, const uint32_t dir, const uint32_t c1, const uint32_t c2)
{
//    float x = dir ? _progress : 1 - _progress;
//    return (i+1) <= x * (w+1) ? c1 : c2;
    float x = dir ? _progress : kbRGB_PRE_MAX - _progress;
    return (i+1)*kbRGB_PRE_MAX <= x * (w+1) ? c1 : c2;
}
void kbRGB_Int_Swap(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 交换
{
    struct kbRGB_Int_Swap_t *const Swap = &params->Simple_Swap;
    //int reverse = 1;
    uint32_t c1;
    uint32_t c2;
    uint32_t dir = 0;
    //double whole;
    uint32_t old_dir = params->Simple.old_dir;
    c1 = Swap->random1;
    c2 = Swap->random2;
    //dir = (int) whole % 2;
    dir = ((int) params->time>>kbRGB_PRE_BIT) % 2;
    //double _progress = modf(kbRGB_time, &whole);
    int32_t _progress = (params->time&kbRGB_PRE_MAX);///(float)kbRGB_PRE_MAX;
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Swap_GetColor(reverse ? cols - col_id - 1: col_id, cols, _progress, dir, c1, c2);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    params->time += 0.1 * ((params->Speed+speed*3)<<kbRGB_PRE_BIT) / (float) params->FPS;

    if(old_dir == 0 && dir == 1)
    {
        Swap->random1 = RandomRGBColor();
    }
    else if(old_dir == 1 && dir == 0)
    {
        Swap->random2 = RandomRGBColor();
    }

    params->Simple.old_dir = dir;
}

/*************************************************************************************************\
|        Visor [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Int_Visor_Init(struct kbRGB_Int_Params *const params)    // 遮阳板
{
    struct kbRGB_Int_Visor_t *const Visor = &params->Simple_Visor;
    params->Speed = 20;
    params->Slider2Val = 20;
    Visor->random1 = RandomRGBColor();
    Visor->random2 = RandomRGBColor();
}
static uint32_t Visor_GetColor(const uint16_t i, float count, const int step, const int32_t p_step, const int32_t  width, const uint32_t c0, const uint32_t c1)
{
    // Constraint absolute minimum size
    //float w = std::max<float>(1.5/count, width);
    //width = width/(float)100;
    int32_t w = 1.5*100/count;
    if(w<width) w=width;
    int32_t x_step = p_step/(float)kbRGB_PRE_MAX * (100+ 4.*w) - 1.5*w;

    // dont divide by zero
    if(count <= 1)
    {
        count = 2;
    }

    int32_t x = i*100 / (count-1);
    int32_t dist = (x_step - x);


    // fade the head
    if(dist < 0)
    {
        //float l = std::max<float>(0.,std::min<float>((w+dist)/w, 1.));
        float l = (w+dist)/(float)w;
        if(l>1.0) l=1.0;
        if(l<0) l=0;
        return step ? Enlight(c1, l) : Enlight(c0, l) ;
    }

    // fade the tail
    if(dist > w)
    {
        //float l = std::max<float>(0.,std::min<float>(1.-((dist-w)/w), 1.));
        float l = 1.-((dist-w)/(float)w);
        if(l>1.0) l=1.0;
        if(l<0) l=0;
        return step ? Enlight(c0, l) : Enlight(c1, l) ;
    }

    // interpolate colors

    //float interp = std::min<float>(std::max<float>((w-dist)/w, 0.),1.);
    float interp = (w-dist)/(float)w;
    if(interp<0) interp=0;
    if(interp>1) interp=1;

    return step ? Interpolate(c0,c1,interp) : Interpolate(c1,c0,interp);

}
//#define  last_step    kbrgb_Int_Params.Simple_last_step
void kbRGB_Int_Visor(const uint16_t cols, const uint16_t rows, const uint8_t speed, const int reverse, struct kbRGB_Int_Params *const params)    // 遮阳板
{
    struct kbRGB_Int_Visor_t *const Visor = &params->Simple_Visor;
    // Caculate a few things outside the loop
    //progress += 0.01 * (Speed<<kbRGB_PRE_BIT) / ((float)FPS);
    params->progress +=  ((params->Speed+speed*5)<<kbRGB_PRE_BIT) / (100*params->FPS);

    //float width = 0.01 * Slider2Val;          // [0-1] float, size of the visor
    //h_width = 0.5 * width;              // [0-0.5] float, half width
    //float p = (kbRGB_progress - (long) kbRGB_progress);   // [0-1] float, 0 to 1 progress
#if 0
    float p = (progress&kbRGB_PRE_MAX)/(float)kbRGB_PRE_MAX;
    int step = p < 0.5;                     // p in [0-0.5] = fist step, p in [0.5-1] = second step
    float p_step =  step ? 2.*p : 2.*(1.- p); // [0-1] float, 0 to 1 progress within the step
#endif
    int32_t p = (params->progress&kbRGB_PRE_MAX);
    int step = p < ((kbRGB_PRE_MAX+1)>>1);                     // p in [0-0.5] = fist step, p in [0.5-1] = second step
    int32_t p_step =  step ? 2.*p : 2.*(kbRGB_PRE_MAX- p); // [0-1] float, 0 to 1 progress within the step


    int flipping = (Visor->last_step != step);

    if(flipping) Visor->last_step = step;

    if(flipping)//if(flipping && RandomColorsEnabled)
    {
        Visor->random1 = RandomRGBColor();
        Visor->random2 = RandomRGBColor();
    }
//    else if(!RandomColorsEnabled)
//    {
//        c0 = UserColors[0];
//        c1 = UserColors[1];
//    }
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Visor_GetColor(col_id, cols, step, p_step, /*width*/params->Slider2Val, Visor->random1, Visor->random2);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
}


